/*****************************************************************************
 * $Workfile:: serial.c                                                      $
 * $Revision:: 19                                                            $
 * $JustDate::  3/21/12                                                      $
 *
 *      GENIE V ECU
 *      serial communication interface
 *
 *****************************************************************************/

#include "common.h"
#include "serial.h"
#include "main.h"
#include "switches.h"
#include "display.h"
#include "eeprom.h"
#include "control.h"
#include "joystick.h"
#include "alarm.h"
#include "nvm.h"
#include "faults.h"
#include "battery.h"
#include "fet.h"

#define RX_ENABLE   {vSetStatusSciRxEn(0);}//{IO.PDR7.BIT.B5 = 0;}
#define RX_DISABLE  {vSetStatusSciRxEn(1);}//{IO.PDR7.BIT.B5 = 1;}

#define PUT(x)  { vSciSentByte(x); }
#define DISABLE_SCI3_INT  {vDisSciTxInt();vDisSciRxInt();}//(SCI3.SCR3.BYTE &= ~(0x80 | 0x40 | 0x4))


#define PROTA_MAX_PACKET_LEN  (1 << 4)  // 10 minimum
#define PROTB_MAX_PACKET_LEN  (1 << 5)  // 16 bytes

static struct
{
   UINT8 abData[ PROTA_MAX_PACKET_LEN];
   UINT8 bIndex;
   UINT8 bLostPacketCount;
   UINT8 bCycleTimer;
   UINT8 bInitCycles;
   UINT8 bBatteryTest;
   enum
   {
      OPTION = 0
      , OPTION_REPLAY
      , WATCHDOG
      , WATCHDOG_REPLAY_NOP
      , WATCHDOG_REPLAY
   } eState;
} ProtA;

xfer_t xfer;
//struct ST_GPS_CONTROL stGpsControl = {100, 0};

static UINT8 PROTA_Crc(void);
static UINT8 PROTA_KeyMessages(void);
static void PROTA_LedMessages(void);
static void PROTA_Init(void);
static void PROTA_task(void);
//static void PROTB_Init( void );
//static void PROTB_task( void);

static void PROTA_GpsMessages(void);   // organize information to GPS module, by Shawn@20160812

void serial_init(void)
{
   xfer.eBaud = eBAUD_9600;
   xfer.eState = eINIT;
   xfer.fLinkActive = 0;
   xfer.fPlatformInit = 0;
   PROTA_Init();
}


// called every 10 millisecond

void serial(void)
{
   switch(xfer.eState)
   {
      default:
      case eINIT:
         RX_ENABLE;
         /*
            IO.PMR1.BIT.TXD = 0;    // disable p22 as TxD
            SCI3.SCR3.BYTE = 0;     // Disable receiver, transmitter, and all interrupts
            SCI3.SMR.BYTE = 0;         // Set Async, 8Bit, NO Parity, 1 Stop, MP mode disabled,
                                    // Set Clock devider n = 0;
            SCI3.BRR  = xfer.eBaud; // Initialize Baud Rate
         */
         R_UART0_Start();
         xfer.eState = ePROT_A;  // set default protocol
         break;
      case ePROT_A:
         PROTA_task();
         break;
   }
}



/*
 * interrupt routine
 */
void INT_serial_receive(void)
{
   if(ePROT_A == xfer.eState)
   {
      /*    if ( 0 != (SCI3.SSR.BYTE & 0x38) ) // OER, PER, FER
            {
               SCI3.SSR.BYTE &= ~0x38; // clear errors;
      #if 0
               if ( ProtA.bFrameErrorCount < 0xFF)
               {
                  ProtA.bFrameErrorCount++;
                  }
      #endif
            }
            */

      //    if ( SCI3.SSR.BIT.RDRF && SCI3.SCR3.BIT.RIE)
      //    {

      if(ProtA.bIndex < PROTA_MAX_PACKET_LEN)
      {
         ProtA.abData[ ProtA.bIndex] = u8SciGetByte();
         ProtA.bIndex++;
      }
      //    }
   }
   else
   {
      DISABLE_SCI3_INT;
   }
   vClearRxIntFlag();    /* clear INTSR0 interrupt flag */
}

void INT_serial_send(void)
{
   if(ePROT_A == xfer.eState)
   {
      PUT(ProtA.abData[ ProtA.bIndex]);
      if(ProtA.bIndex)
      {
         ProtA.bIndex--;
      }
      else
      {
         vDisSciTxInt();    /* disable INTST0 interrupt */
         vEnSciRxInt();
         //SCI3.SCR3.BIT.TIE = 0; // disable tx interrupt.
      }
   }
   else
   {
      DISABLE_SCI3_INT;
   }
   vClearTxIntFlag();    /* clear INTST0 interrupt flag */
}

static void
PROTA_Init(void)
{
   UINT8 ix;
   UINT8 * pb;

   pb = (UINT8 *)&ProtA;

   for(ix = 0; ix < sizeof(ProtA); ix++)
   {
      *pb++ = 0;
   }
}
/*
 * invoked every 10 millisecond
 */
// TODO: ? rx & tx buffers or shared tx/rx buffer ?
// TODO: ? add a multiple option read to avoid data corruption ?
static void
PROTA_task(void)
{
   static UINT8 message_cnt = 1; // counter of messages send to PCU, by Shawn@20160812

   if(ProtA.bCycleTimer)
   {
      ProtA.bCycleTimer--;
   }

   switch(ProtA.eState)
   {
      default:
      case OPTION:
         if(0 == ProtA.bCycleTimer)
         {
            ProtA.bCycleTimer  = (300 / 10); // 100 millisecond to wait for option
            ProtA.bIndex = 0;
            if(!u8IsTxReady())
            {;}
            else
            {
               PUT(0xA3);           // send one byte A3 for upside down Platform display A0 Normal.
               vEnSciRxInt();    /* enable INTSR0 interrupt */
            }
            //SCI3.SCR3.BIT.RIE  = 1; // enable rx int
            ProtA.eState = OPTION_REPLAY;
         }
         break;

      case OPTION_REPLAY:
         if(3 == ProtA.bIndex
            && 0xA3 == ProtA.abData[ 0]       // IF upside down, A3 went out, A0 is returned.
            && 0xA0 == (ProtA.abData[ 1] & 0xf0))   // this line checks the out byte and the returned byte
         {
            //
            // save option byte only once after power is up
            // the re-initialization of serial protocol after sync has been lost
            // does'nt update option byte.
            //
            if(!xfer.fLockOption)
            {
               // *((UINT8 *)&lift_options) = ProtA.abData[ 2]; todo remove it
               if(ProtA.abData[2] & 0x1)
               {
                  xfer.fSuspectGenVI = 1;
               }
               else
               {
                  xfer.fSuspectGenVI = 0;
               }
               xfer.fLockOption = 1;
            }
            xfer.fLinkActive = 1;
            ProtA.bLostPacketCount = 0;
            ProtA.bCycleTimer  = (50 / 10); // 50 millisecond
            ProtA.bInitCycles = 11;        // number of init cycles
            ProtA.eState = WATCHDOG;
         }
         else if(ProtA.bCycleTimer)
         {
#if 0
            ProtA.bCycleTimer--;

#else

            // just do nothing
#endif
         }
         else if(ProtA.bLostPacketCount < 10)
         {
            ProtA.bLostPacketCount++;
            ProtA.eState = OPTION;
         }
         else
         {
            serial_init();       // it will reinit all protocols
            xfer.fLostCommunication = 1;
            NVM_SaveFault(FAULT_COM_ERROR);
         FaultWord1 |= 0x0002;
            // end Shawn@20160812
         unCanFault1.word |= 0x0002;
         }
         break;
      case WATCHDOG:      // every 50 millisecond
         if(0 == ProtA.bCycleTimer)
         {
            if(ProtA.bInitCycles)
            {
               ProtA.bInitCycles--;
            }
            else
            {
               xfer.fPlatformInit = 1;
               xfer.fLostCommunication = 0;
            FaultWord1 &= ~0x0002;
            unCanFault1.word &=~0x0002;
            }
            //SCI3.SSR.BYTE &= ~0x38;  // clear oer,per,fer, and rdrf
            //ProtA.abData[ 0] = SDR01L; // clear RDRF

            ProtA.abData[ 0] = u8SciGetByte(); // clear RDRF
            ProtA.bIndex = 0;
            ProtA.bCycleTimer = (50 / 10); // 50 millisecond
            if(xfer.fSuspectGenVI)
            {
               ProtA.bIndex = 0;
               if(!u8IsTxReady())
               {;}
               else
               {
                  ProtA.bIndex = 0;
                  PUT(0x1A);           // send one byte A3 for upside down Platform display A0 Normal.
                  vEnSciRxInt();    /* enable INTSR0 interrupt */
               }
            }
            //                else
            //                {
            //                    PUT(0x15); //send one byte
            //                }
            //SCI3.SCR3.BIT.RIE  = 1;    // enable rx int
            ProtA.eState = WATCHDOG_REPLAY_NOP;
         }
         break;
      case WATCHDOG_REPLAY_NOP:
         ProtA.eState = WATCHDOG_REPLAY;
         break;
      case WATCHDOG_REPLAY:
         DISABLE_SCI3_INT;
         ProtA.eState = WATCHDOG;
         if(PROTA_KeyMessages())
         {
            if(ProtA.bLostPacketCount >= 6)
            {
               serial_init();       // it will reinit all protocol variables
               JoystickInit();     // it will clear all platfrom parameters
               xfer.fLostCommunication = 1;
               xfer.fSuspectGenVI ^= 1; //try the new protocol
               NVM_SaveFault(FAULT_COM_ERROR);
            }
            else
            {
               ProtA.bLostPacketCount++;
            }
         }
         else
         {
            if(ProtA.bLostPacketCount)
            {
               ProtA.bLostPacketCount--;
            }
            // communicate with PCU and GPS alternatively, by Shawn@20160812
#if 0
            if ( 4 == message_cnt )
            {
               // prepare status messages
               PROTA_GpsMessages();
               message_cnt = 0;
            }
            else
#endif
            {
               // prepare led messages
               PROTA_LedMessages();
               message_cnt++;
            }
            // end, by Shawn@20160812
            PUT(ProtA.abData[ ProtA.bIndex]);
            ProtA.bIndex--;
            // transmitter uses reverse buffer
            vEnSciTxInt();    /* enable INTST0 interrupt */
            //SCI3.SCR3.BIT.TIE  = 1;     // enable tx int, disabled by PROTA_int()
         }
         break;
   }
}


static UINT8
PROTA_KeyMessages(void)
{
   static UINT8 SwitchEnableTimer = 0;
   static UINT8 firstmessage = 0;
   if(xfer.fSuspectGenVI)
   {
      if(!(10 == ProtA.bIndex
           && 0x1A == ProtA.abData[ 0] // echo byte
           && 0x15 == ProtA.abData[ 1] // watchdog replay
           && 0x20 == (ProtA.abData[ 2] & 0xf0)
           && 0x30 == (ProtA.abData[ 3] & 0xf0)
           && 0x40 == (ProtA.abData[ 4] & 0xf0)
           && 0x50 == (ProtA.abData[ 5] & 0xf0)
           && 0x60 == (ProtA.abData[ 6] & 0xf0)
           && 0x70 == (ProtA.abData[ 7] & 0xf0)
           && 0xC0 == (ProtA.abData[ 8] & 0xf0)
           && PROTA_Crc() == ProtA.abData[ 9]))
      {
         return 1;
      }
      else if(!firstmessage)     // throw out first message
      {
         firstmessage = 1;
         return 1;
      }

   }
   else
      // if ( !(10 == ProtA.bIndex
      //                && 0x15 == ProtA.abData[ 0]   // echo byte
      //                && 0x1A == ProtA.abData[ 1]   // watchdog replay
      //                && 0x20 == (ProtA.abData[ 2] & 0xf0)
      //                && 0x30 == (ProtA.abData[ 3] & 0xf0)
      //                && 0x40 == (ProtA.abData[ 4] & 0xf0)
      //                && 0x50 == (ProtA.abData[ 5] & 0xf0)
      //                && 0x60 == (ProtA.abData[ 6] & 0xf0)
      //                && 0x70 == (ProtA.abData[ 7] & 0xf0)
      //                && 0xC0 == (ProtA.abData[ 8] & 0xf0)
      //                && PROTA_Crc( ) == ProtA.abData[ 9]) )
   {
      return 1;      // wrong message
   }

   // extract data

   // message ID 0x20
   SW_HIGH = (ProtA.abData[ 2] & 0x4) ? 1 : 0;        // HIGH torque mode
   LIFT_DRIVE = (ProtA.abData[ 2] & 0x1) ? 1 : 0;     // LIFT button/mode
   // message ID 0x30
   SW_HORN = (ProtA.abData[ 3] & 0x1) ? 1 : 0;        // HORN button
   JS_NEUTRAL = (ProtA.abData[ 3] & 0x2) ? 1 : 0;     // joystick neutral position
   if(xfer.fSuspectGenVI)
   {
      SW_LIFT    = (ProtA.abData[ 3] & 0x4) ? 1 : 0;     // lift button
      SW_DRIVE   = (ProtA.abData[ 3] & 0x8) ? 1 : 0;     // drive button
   }
   //    else
   //    {
   //        SW_LIFT    =  0;     // lift button
   //        SW_DRIVE   =  0;     // drive button
   //    }

   // message IDs 0x40 && 0x50 reserved
   ROCKER_RAW_COUNTS = ((ProtA.abData[ 4] & 0xf) << 4) | (ProtA.abData[ 5] & 0xf);

   // message ID 0x60;
   SW_ENABLE_RAW = ((ProtA.abData[ 6] & 0x1) ? 1 : 0);      // joystick interlock
   if(LIFT_Data.fLiftDrive || !LEVEL_SENSOR || !(LIFT_Data.hd_fet & (FET_FORWARD | FET_REVERSE)))
      // if (((LIFT_Data.fLiftDrive && ( (ELEC3 != lift_setup.bLiftType) || !(LIFT_Data.hd_fet & FET_UP)))
      //       ||    (!LIFT_Data.fLiftDrive && !(LIFT_Data.hd_fet & (FET_FORWARD | FET_REVERSE) ) ) )
      //       || !LEVEL_SENSOR )
   {
      SwitchEnableTimer = 0;
   }
   else if(SW_ENABLE_RAW)
   {
      //    if (LIFT_Data.hd_fet & FET_UP)
      //       SwitchEnableTimer = (UINT8)(5080l/LIFT_Data.pDriveCurve->nDecel); // 3 seconds/50 millisecond intervals
      //    else
      SwitchEnableTimer = (UINT8)(25400l / LIFT_Data.pDriveCurve->nDecel); // 3 seconds/50 millisecond intervals
   }
   else if(SwitchEnableTimer)
   {
      SwitchEnableTimer--;
   }
   SW_ENABLE = (SW_ENABLE_RAW || SwitchEnableTimer);     // joystick interlock
   //    SW_ENABLE_RAW = (( ProtA.abData[ 6] & 0x1 ) ? 1: 0);     // joystick interlock
   SW_RIGHT  = (ProtA.abData[ 6] & 0x2) ? 1 : 0;      // joystick right
   SW_LEFT   = (ProtA.abData[ 6] & 0x4) ? 1 : 0;      // joystick left
   JS_DIRECTION  = (ProtA.abData[ 6] & 0x8) ? 1 : 0;  // joystick direction


   // messages IDs 0x70 and 0xC0
   JOYSTICK_RAW_COUNTS = ((ProtA.abData[ 7] & 0xf) << 4) | (ProtA.abData[ 8] & 0xf);

   JoystickAdjust();

   //    RockerAdjust( );

   return 0;
}

#ifdef DEBUG_SERIAL_DUMP
static struct
{
#define DEBUG_SIZE  128
   UINT8   abData[ 128 ];
   UINT8   index;
   UINT8   count;
   UINT8   what;
   enum
   {
      DEBUG_INIT = 0
      , DEBUG_SEND
   } eState;
} DEBUG_Local;

static void
PROTA_debug_encode(UINT8 type, UINT8 * src, UINT8 count)
{
   int ix, jx;
   UINT8 byte;
   UINT8 sum;

   ix =  count * 2 + 10;

   if(ix > DEBUG_SIZE)
   {
      DEBUG_Local.count = 0;
      return;
   }

   sum = type + count;
   for(ix = 0; ix < count; ix++)
   {
      sum +=  src[ ix];
   }

   sum = -sum;

   jx = 0;

   DEBUG_Local.abData[ jx++] = 0xE7;
   DEBUG_Local.abData[ jx++] = 0xF0 | ((type >> 4) & 0xF);
   DEBUG_Local.abData[ jx++] = 0xF0 | (type & 0xF);
   DEBUG_Local.abData[ jx++] = 0xF0 | ((count >> 4) & 0xF);
   DEBUG_Local.abData[ jx++] = 0xF0 | ((count) & 0xF);

   for(ix = 0; ix < count; ix++)
   {
      byte = *src++;

      DEBUG_Local.abData[ jx++] = 0xF0 | ((byte >> 4) & 0xF);
      DEBUG_Local.abData[ jx++] = 0xF0 | ((byte) & 0xF);
   }
   DEBUG_Local.abData[ jx++] = 0xF0 | ((sum >> 4) & 0xF);
   DEBUG_Local.abData[ jx++] = 0xF0 | ((sum) & 0xF);
   DEBUG_Local.abData[ jx++] = 0xE6;

   DEBUG_Local.count = jx;
}
#endif

static void
PROTA_LedMessages(void)
{
   // buffer in reverse is filled in
   // reverse direction!!
   ProtA.bIndex = 8;
#ifdef DEBUG_SERIAL_DUMP
   switch(DEBUG_Local.eState)
   {
      default:
      case DEBUG_INIT:
         if(MAIN_OPERATIONAL == MAIN_Data.eState)
         {
            switch(DEBUG_Local.what)
            {
               default:
               case 0:
                  PROTA_debug_encode(DEBUG_Local.what,
                                     (UINT8 *)&BATTERY_Local.anTripVoltage,
                                     sizeof(BATTERY_Local.anTripVoltage));
                  DEBUG_Local.what = 1;
                  break;
               case 1:
                  PROTA_debug_encode(DEBUG_Local.what,
                                     (UINT8 *)&BATTERY_Local.anTripVoltageMin,
                                     sizeof(BATTERY_Local.anTripVoltageMin));
                  DEBUG_Local.what = 2;
                  break;
               case 2:
                  PROTA_debug_encode(DEBUG_Local.what,
                                     (UINT8 *)&BATTERY_Local.nAverage,
                                     sizeof(BATTERY_Local.nAverage));
                  DEBUG_Local.what = 3;
                  break;
               case 3:
                  PROTA_debug_encode(DEBUG_Local.what,
                                     (UINT8 *)&BATTERY_Local.wAdc,
                                     sizeof(BATTERY_Local.wAdc));
                  DEBUG_Local.what = 0;
                  break;
            }
            DEBUG_Local.eState = DEBUG_SEND;
            DEBUG_Local.index = 0;
         }
         break;

      case DEBUG_SEND:
         {
            UINT8 jx;
            UINT8 left;

            left = 7;
            if(left > DEBUG_Local.count)
            {
               left = DEBUG_Local.count;
               DEBUG_Local.eState = DEBUG_INIT;
            }
            DEBUG_Local.count -= left;

            ProtA.bIndex += left;
            jx = ProtA.bIndex;
            while(left--)
            {
               ProtA.abData[ jx--] = DEBUG_Local.abData[ DEBUG_Local.index++];
            }
         }
         break;
   }
#endif

   ProtA.abData[ 8] = 0xD0 | ((DISPLAY_Platform.Display.field.bSevenLeft >> 4) & 0xf);
   ProtA.abData[ 7] = 0xD0 | (DISPLAY_Platform.Display.field.bSevenLeft & 0xf);
   ProtA.abData[ 6] = 0xD0 | ((DISPLAY_Platform.Display.field.bSevenRight >> 4) & 0xf);
   ProtA.abData[ 5] = 0xD0 | (DISPLAY_Platform.Display.field.bSevenRight & 0xf);
   ProtA.abData[ 4] = 0xD0 | (DISPLAY_Platform.bButtonLeds & 0xf);

   // transmitter uses reverse buffer
   // message 0x80
   ProtA.abData[ 3] = 0x80;
   if(ALARM_Data.fPlatform)
   {
      ProtA.abData[ 3] |= 0x08;
   }

   // add Fault and Ready bits here

   if(DISPLAY_Platform.fReady)
   {
      ProtA.abData[ 3] |= 0x01;
   }
   if(DISPLAY_Platform.fFault)
   {
      ProtA.abData[ 3] |= 0x02;
   }
   // message 0x90
   ProtA.abData[ 2] = (LIFT_Data.fHighMode && !SLEEP_Data.fSleep) ? 0x91 : 0x90;

   if(NVM_LiftDrive.bData && ProtA.bInitCycles)
   {
      ProtA.abData[ 2] |= 0x4 | 0x2;
   }


   ProtA.abData[ 1] = 0xB0 | (DISPLAY_Platform.bChargePercent & 0xf);      // set to  full battery charge

   // reverse joystick direction when lifting
   if(MAIN_Data.eState == MAIN_SPEED_ADJUST)
   {
	  if(JOYSTICK_RAW_COUNTS > 10)
	    ProtA.abData[ 0] = 0xF0 | (lift_options.fUS_option ? 0x00 : 0x04);
	  else
		ProtA.abData[ 0] = 0xF0 | (lift_options.fUS_option ? 0x04 : 0x00);
   }
   else
      ProtA.abData[ 0] = 0xF0 | (lift_options.fUS_option ? 0x04 : 0x00);
}

static UINT8
PROTA_Crc(void)
{
   UINT16 CRCTemp;
   UINT16 ix;

   CRCTemp = 0;

   for(ix = 2; ix < 8; ix++)
   {
      CRCTemp ^= (UINT16)ProtA.abData[ ix];
      CRCTemp <<= 1;
      if(CRCTemp > (UINT16)0xff)
      {
         CRCTemp ^= (UINT16)0x119;
      }
   }
   return ((UINT8)CRCTemp ^ (UINT8)(ProtA.abData[ ix]));
}

static void
PROTA_GpsMessages(void)
{
   static UINT8 info_index = 0;  // information to GPS module
   UINT16 temp = 0;

   ProtA.bIndex = 11;
   ProtA.abData[11] = 0xFF;
   ProtA.abData[10] = 0xF8;

      switch(info_index)
      {
         default:
         info_index = 5;
            break;
         case 0:
            ProtA.abData[9] = ((FETS >> 12) & 0x0F) + 0x20;
            ProtA.abData[8] = ((FETS >>  8) & 0x0F) + 0x20;
            ProtA.abData[7] = ((FETS >>  4) & 0x0F) + 0x20;
            ProtA.abData[6] = ((FETS      ) & 0x0F) + 0x20;
            ProtA.abData[5] = 0x20;//((LIFT_Data.calc_pwm1 >> 12) & 0x0F) + 0x20;
            ProtA.abData[4] = 0x20;//((LIFT_Data.calc_pwm1 >>  8) & 0x0F) + 0x20;
            ProtA.abData[3] = 0x20;//((LIFT_Data.calc_pwm1 >>  4) & 0x0F) + 0x20;
            ProtA.abData[2] = 0x20;//((LIFT_Data.calc_pwm1      ) & 0x0F) + 0x20;
            break;
         case 1:
            ProtA.abData[9] = ((ui_Measured_Scissor_Angle_Main >> 12) & 0x0F) + 0x30;
            ProtA.abData[8] = ((ui_Measured_Scissor_Angle_Main >>  8) & 0x0F) + 0x30;
            ProtA.abData[7] = ((ui_Measured_Scissor_Angle_Main >>  4) & 0x0F) + 0x30;
            ProtA.abData[6] = ((ui_Measured_Scissor_Angle_Main) & 0x0F) + 0x30;
            ProtA.abData[5] = ((ui_Measured_Cylinder_Pressure >> 12) & 0x0F) + 0x30;
            ProtA.abData[4] = ((ui_Measured_Cylinder_Pressure >>  8) & 0x0F) + 0x30;
            ProtA.abData[3] = ((ui_Measured_Cylinder_Pressure >>  4) & 0x0F) + 0x30;
            ProtA.abData[2] = ((ui_Measured_Cylinder_Pressure) & 0x0F) + 0x30;
            break;
         case 2:
            ProtA.abData[9] = ((SWITCH_Data.wSwitches.word >> 12) & 0x0F) + 0x40;
            ProtA.abData[8] = ((SWITCH_Data.wSwitches.word >>  8) & 0x0F) + 0x40;
            ProtA.abData[7] = ((SWITCH_Data.wSwitches.word >>  4) & 0x0F) + 0x40;
            ProtA.abData[6] = ((SWITCH_Data.wSwitches.word) & 0x0F) + 0x40;
            ProtA.abData[5] = 0x40;//((UINT8)SW_DRIVE<<2) + ((UINT8)SW_LIFT<<1) + (UINT8)SW_LEFT + 0x40;
            ProtA.abData[4] = 0x40;//((UINT8)SW_RIGHT<<3) + ((UINT8)SW_ENABLE<<2) + ((UINT8)SW_HORN<<1) + (UINT8)SW_HIGH + 0x40;
            ProtA.abData[3] = ((MAIN_Data.eState >>  4) & 0x0F) + 0x40;
            ProtA.abData[2] = ((MAIN_Data.eState      ) & 0x0F) + 0x40;
            break;
         case 3:
            ProtA.abData[9] = ((BATTERY_Local.nAverage >> 12) & 0x0F) + 0x50;
            ProtA.abData[8] = ((BATTERY_Local.nAverage >>  8) & 0x0F) + 0x50;
            ProtA.abData[7] = ((BATTERY_Local.nAverage >>  4) & 0x0F) + 0x50;
            ProtA.abData[6] = ((BATTERY_Local.nAverage) & 0x0F) + 0x50;
            ProtA.abData[5] = ((n_Pressure_Percent_of_Maximum >> 12) & 0x0F) + 0x50;
            ProtA.abData[4] = ((n_Pressure_Percent_of_Maximum >>  8) & 0x0F) + 0x50;
            ProtA.abData[3] = ((n_Pressure_Percent_of_Maximum >>  4) & 0x0F) + 0x50;
            ProtA.abData[2] = ((n_Pressure_Percent_of_Maximum      ) & 0x0F) + 0x50;
            break;
         case 4:
            ProtA.abData[9] = ((ui_Measured_Cylinder_Pressure2 >> 12) & 0x0F) + 0x70;
            ProtA.abData[8] = ((ui_Measured_Cylinder_Pressure2 >>  8) & 0x0F) + 0x70;
            ProtA.abData[7] = ((ui_Measured_Cylinder_Pressure2 >>  4) & 0x0F) + 0x70;
            ProtA.abData[6] = ((ui_Measured_Cylinder_Pressure2      ) & 0x0F) + 0x70;
            ProtA.abData[5] = ((DriveCurrent >> 12) & 0x0F) + 0x70;
            ProtA.abData[4] = ((DriveCurrent >>  8) & 0x0F) + 0x70;
            ProtA.abData[3] = ((DriveCurrent >>  4) & 0x0F) + 0x70;
            ProtA.abData[2] = ((DriveCurrent      ) & 0x0F) + 0x70;
            break;
      }
   if ( (++info_index) > 4 )
   {
      info_index = 0;
   }

   temp = ProtA.abData[9] + ProtA.abData[8] + ProtA.abData[7] + ProtA.abData[6] + \
          ProtA.abData[5] + ProtA.abData[4] + ProtA.abData[3] + ProtA.abData[2];
   ProtA.abData[1] = (temp & 0xF) + 0xF0;
   ProtA.abData[0] = 0xF0;
}
